home *** CD-ROM | disk | FTP | other *** search
- PAGE 60,132
- TITLE BR1BIOS - Serial interface for COM1
-
- ;Interupt driven RS232 serial port routines.
- ; these routines replace the BIOS rs232 calls with a version that has
- ; interupt driven character receive, and can thus operate at considerably
- ; higher speeds than the standard bios calls (int 14h).
- ; Added break function (7).
- ; Set up the works
-
- everything SEGMENT PUBLIC
- ASSUME CS: everything ; These assumptions are for the
- ASSUME DS: everything ; assembler's benefit. The code
- ASSUME ES: nothing ; jerks things around as it pleases
- ASSUME SS: nothing
-
- ; Program start and buffer declares
-
- ORG 100H
- foo: jmp start ; Entry point
-
- org 0 ; Back up so we can use this all as
- ; buffer space
-
- ; Area where things are declared
-
- include constbios.asm
-
- dummy: ; Dummy labels to provide a structure
-
- i_buf db buffer_size dup(?) ; Buffer (1 per com card)
- i_buf_e: ; End of buffer
- comnumber db ? ; Comm number - 1
- flags db ? ; Flag byte
- last_rs db ? ; Last receive status
- hiv db ? ; Hardware interrupt vector
- int_mask db ? ; Mask for 8259
- baseaddr dw ? ; Base port address
- i_count dw ? ; # of chars in buffer
- i_buf_in dw ? ; Buffer in pointer
- i_buf_out dw ? ; Buffer out pointer
- dummy_end:
-
- org dummy ; Back up over this
-
- ; These are the things to work with
-
-
- db buffer_size dup(?) ; Buffer (1 per com card)
- db 0 ; Com number
- db 0 ; Flags
- db 0 ; Last receive status
- db 0ch ; Hardware interrupt vector
- db 0efh ; Mask
- dw 03f8h ; Base port address
- dw 0 ; # of chars in buffer
- dw ? ; Buffer in pointer
- dw ? ; Buffer out pointer
-
-
- comend: ; Marker for last com port
-
- ; Static variables
-
- old_bios_vector dw ? ; Save previous interrupt vector
- dw ?
-
- divisor_table LABEL WORD
- dw 1047 ; 110
- dw 768 ; 150
- dw 384 ; 300
- dw 192 ; 600
- dw 96 ; 1200
- dw 48 ; 2400
- dw 24 ; 4800
- dw 12 ; 9600
-
-
- ; Our BIOS handler
-
- rsint:
-
- ASSUME DS: NOTHING ; Don't use DS
- ASSUME ES: NOTHING ; Don't use ES
- ASSUME SS: NOTHING ; Don't use SS
-
- ; Not disabled please
-
- sti
-
- ; See if this is our vector?
-
- push bp ; Save BP over our loop
- xor bp,bp
- cmp dl,cs:comnumber[bp] ; Is this our port?
- je rsint_ours ; Yes...
-
- ; Not us.. Pop things out and call regular handler
-
- pop bp
- jmp cs:dword ptr old_bios_vector
-
- ; BP now contains the pointer to the com block... See what
- ; the user has requested and we may or may not do it.......
-
- rsint_ours:
-
- push dx ; We need the DX register
- push cx ; We need the CX register
- push bx ; We need the BX register
-
- mov cx,baseaddr[bp] ; Get base address for chip
-
- or ah,ah ; Initialize?
- je rsint_init ; Yes...
-
- dec ah ; 1 = Send character
- jz rsint_send
-
- dec ah ; 2 = Receive character
- jz rsint_recv_jmp
-
- dec ah ; 3 = Status request
- jz rsint_status_jmp
-
- dec ah ; 4 = Inquiry
- jz rsint_inquiry_jmp
-
- dec ah
- dec ah
- dec ah
- jz rsint_break_jmp ; 7 = send break
-
- jmp rsint_exit ; Nope..
-
- rsint_recv_jmp:
- jmp rsint_recv
- rsint_status_jmp:
- jmp rsint_status
- rsint_inquiry_jmp:
- jmp rsint_inquiry
- rsint_break_jmp:
- jmp rsint_break
-
- ; Interrupt exit
-
- rsint_exit:
-
- pop bx ; Restore registers
- pop cx
- pop dx
- pop bp
- iret ; and leave
-
- ; Init..
-
- rsint_init:
-
-
- mov ah,al ; Save the parms for later
-
- mov bl,ah ; Look up the baud rate
- mov cl,4 ; parameter
- rol bl,cl
- and bx,0eh
- mov bx,divisor_table[bx]
-
- mov cx,baseaddr[bp] ; Get base address for chip
- mov dx,cx ; Address of LCR
- add dx,lcr_8250
- mov al,10000000B ; Enable access to divisor
- out dx,al
-
- mov dx,cx ; Address of lower divisior half
- add dx,dll_8250
- mov al,bl ; Put lower half
- out dx,al
-
- mov dx,cx ; Address of upper divisior half
- add dx,dlm_8250
- mov al,bh ; Put upper half
- out dx,al
-
- mov al,ah ; Get parms back
- and al,1fh ; Throw away baud rate
- mov dx,cx ; Address of LCR
- add dx,lcr_8250
- out dx,al ; Output the parms
-
- ; Turn on the port
-
- mov dx,cx ; Compute port address for the MCR
- add dx,mcr_8250
- mov al,00001011B ; Raise DTR, RTS & OUT2
- out dx,al ; OUT2 turns on interrupts
-
- jmp rsint_status ; Now just status please
-
- ; Send a character
-
- rsint_send:
-
- mov ah,al ; Save the character to send
-
- rsint_send_loop: ; Loop here until we can send
-
- mov dx,cx ; Compute port address for the MSR
- add dx,msr_8250 ; and then
- in al,dx ; get it into AX
- and al,10h ; CTS
- jz rsint_send_loop
-
- mov dx,cx ; Compute port address for the LSR
- add dx,lsr_8250 ; and then
- in al,dx ; get itinto AX
- and al,20h ; THR empty?
- jz rsint_send_loop ; No.. Loop back
-
- mov al,ah ; Get ready to out character
-
- mov dx,cx ; Compute port address for the THR
- ; add dx,thr_8250 ; and then
- out dx,al ; out the character
-
- jmp rsint_status ; Do a status
-
- ; Receive a character
-
- rsint_recv:
-
- MOV BX,i_buf_out[BP] ; Get buffer output pointer
-
- rsint_recv_loop:
-
- CMP i_count[BP],0 ; Anything in buffer?
- JE rsint_recv_loop ; No, Wait for it
-
- rsint_get_char:
-
- MOV AL,CS:[BX] ; Get character from buffer
- PUSH AX ; Save char
- INC BX ; Bump pointer
- DEC i_count[BP] ; Dec char count
- MOV DX,OFFSET i_buf_e ; Compute end of buffer
- ADD DX,BP
- CMP BX,DX ; Have we wrapped the buffer?
- JL test_handshake ; No.. All done
- MOV BX,OFFSET i_buf ; Yes.. Reset pointer
- ADD BX,BP
- test_handshake:
- cmp i_count[BP],80H
- jnb test_full
- mov ah,flags[BP]
- test ah,1 ; Did we RTS off?
- jz test_full ; No, so don't RTS on
- roll_hands:
- mov DX,CX
- add DX,mcr_8250
- in AL,DX
- or AL,00001011B ; Raise DTR, RTS & OUT2
- out DX,AL
- and AH,0FEH ; Remember RTS is on
- mov flags[BP],AH
-
- test_full:
- POP AX ; Restore Char
-
- MOV i_buf_out[BP],BX ; Save pointer
- MOV AH,last_rs[BP] ; Get last LSR from receive
- AND AH,0FEH ; Remove data ready bit
- CMP i_count[BP],0 ; Anything left in buffer?
- JE short_exit ; Nope so leave
- OR AH,1 ; Turn on data ready
- short_exit:
- JMP rsint_exit ; and go leave
-
- ; Status..
-
- rsint_status:
-
- MOV DX,CX ; Compute port address for the LSR
- ADD DX,lsr_8250 ; and then
- IN AL,DX ; get it
-
- AND AL,0FEH ; Remove data ready bit
-
- CMP i_count[BP],0 ; Anything left in buffer?
- JE rsint_status_nodr ; Nope so leave
- OR AL,1 ; Turn on data ready
- rsint_status_nodr:
- MOV AH,AL ; Save LSR
-
- MOV DX,CX ; Compute port address for the MSR
- ADD DX,msr_8250 ; and then
- IN AL,DX ; get it
-
- JMP rsint_exit ; All done
-
- ; Inquiry
- ; (Return AA55H in AX - Just an identification scheme to tell
- ; if this silly driver has been loaded)
-
- rsint_inquiry:
-
- MOV AX,0AA55H
- JMP rsint_exit ; and go leave
-
-
- ; send break
- rsint_break:
- mov dx,cx
- add dx,lcr_8250
- in al,dx
- mov bl,al
- mov al,40h
- out dx,al
- mov cx,0
-
- bkwait: loop bkwait
- mov al,bl
- out dx,al
- jmp rsint_exit
-
- ; 8250 interrupt handler
-
- serint_8250:
-
- PUSH BP
- PUSH DX
- PUSH AX
- PUSH CX ; Save some registers
- PUSH DI
- xor bp,bp
-
- MOV CX,baseaddr[BP] ; Get base address for chip
-
- MOV DX,CX ; Get the IIR
- ADD DX,iir_8250
- IN AL,DX
-
- TEST AL,1 ; Interrupt pending?
- JZ service
- JMP serint_8250_exit ; No leave...
-
- service:
- MOV DX,CX ; Get the LSR
- ADD DX,lsr_8250
- IN AL,DX
- MOV last_rs[BP],AL ; And tuck it away
-
- MOV DX,CX ; Get the RBR
- ; ADD DX,rbr_8250
- IN AL,DX
- MOV DI,i_buf_in[BP] ; Get the buffer pointer
- MOV CS:[DI],AL ; Save the character
- INC DI ; Bump pointer and handle wrap
- MOV AX,OFFSET i_buf_e
- ADD AX,BP
- CMP DI,AX
- JL nowrap
- MOV DI,OFFSET i_buf
- ADD DI,BP
-
- nowrap:
-
- cmp i_count[BP],buffer_full
- jb hand_done
- mov DX,CX
- add DX,mcr_8250
- in al,DX
- and AL,11111100B ; Drop DTR & RTS
- out DX,AL
- mov ah,flags[BP]
- or AH,1 ; Remember RTS is off
- mov flags[BP],AH
-
- hand_done:
-
- CMP DI,i_buf_out[BP] ; Overflow of buffer?
- JNE noover
- OR last_rs[BP],2 ; Overrun indicate
- JMP SHORT serint_8250_exit ; Don't save the updated pointer
- noover:
- MOV i_buf_in[BP],DI ; Save the updated pointer
- inc i_count[BP] ; inc char count
-
- serint_8250_exit:
-
- MOV AL,20H ; Tell 8259 we are done
- OUT pic_cmd_port,AL
- POP DI ; Restore registers
- POP CX
- POP AX
- POP DX
- POP BP
-
- IRET ; Exit
-
- program_end:
-
- ; Main line to initialize.
-
- ASSUME DS: everything
-
- ; Constants only needed by initialization
-
- P3F8 DB 0 ;1 IF CARD AT 3F8
- P2F8 DB 0 ;1 IF CARD AT 2F8
-
- HELLO DB CR,LF
- DB 'COM1BIOS Driver v1.5',CR,LF
- DB 'Dec 18, 1986 '
- DB CR,LF,'$'
-
- BYE DB CR,LF
- DB 'Driver Installed OK!'
- DB CR,LF,'$'
-
- LOADED DB CR,LF
- DB 'COM1BIOS already loaded!'
- DB CR,LF,'$'
-
- COM1F DB CR,LF
- DB 'COM1: board found at 03F8',CR,LF
- DB '$'
-
- COM2F DB CR,LF
- DB 'COM2: board found at 02F8',CR,LF
- DB '$'
-
- NOCOM DB CR,LF
- DB 'COM1: board not found.'
- DB CR,LF
- DB 'Driver NOT installed!',CR,LF
- DB '$'
-
- ; INITIALIZE ROUTINE
-
- start: MOV AX,CS ; Point DS in the right place
- MOV DS,AX
- CALL SINON ;PRINT SIGN ON
-
- MOV DX,00 ;Check to see if support already loaded
- MOV AH,04
- INT 14H
- CMP AX,0AA55H
- JZ EXIT ;Must be loaded
-
- CALL CNFIG ;CHECK CONFIGURATION
- OR AX,AX
- JNZ EREXIT ;JMP IF ERRORS
-
- CALL init_vectors ;SET INTERRUPT VECTORS
-
- CALL SINOFF ;PRINT SIGN OFF
- MOV AL,0 ;set exit code
- MOV DX,OFFSET program_end
- MOV CL,4
- SHR DX,CL
- INC DX
- MOV AH,31H
- INT 21H ; Terminate but stay resident
-
- EREXIT: MOV AL,1 ;set exit code = Error
- MOV AH,4CH
- INT 21H ; Terminate and return
-
- EXIT: LEA DX,LOADED
- MOV AH,9
- INT 21H
- MOV AL,0 ;set exit code = Ok
- MOV AH,4CH
- INT 21H ; Terminate and return
-
-
- ;---------
- CNFIG: CALL FNDPT ;Find Ports
- CMP P3F8,1
- JNE CNF1
- LEA DX,COM1F ;Say COM1 Found
- MOV AH,9
- INT 21H
-
- CNF1: CMP P2F8,1
- JNE CNF2
- LEA DX,COM2F ;Say COM2 Found
- MOV AH,9
- INT 21H
-
- CNF2: CMP P3F8,1
- JE CNF3
- LEA DX,NOCOM ;Say OOPS
- MOV AH,9
- INT 21H
-
- CNFERR: MOV AX,0FFFFH ;Set Error
- RET
-
- CNF3: SUB AX,AX
- RET
-
- ;------------
-
- FNDPT: MOV DX,03F8H
- CALL CKPORT
- CMP BP,0
- JZ PT2
-
- INC P3F8
-
- PT2: MOV DX,02F8H
- CALL CKPORT
- CMP BP,0
- JZ PT3
-
- INC P2F8
-
- PT3: RET
-
-
- ; Check for valid Port
- ; Enter with DX containing Port address
- ; Exit with BP=Port address if port found BP=0 if not found
-
-
- CKPORT: MOV BP,DX ;For return
- MOV CL,DL ;Keep DL Handy
-
- ADD DX,3
- IN AL,DX ;Save old LCR in CH
- MOV CH,AL
-
- MOV AL,80H ;Address Divisor Latch
- OUT DX,AL
- CALL WAIT
-
- MOV DL,CL
- IN AL,DX
- MOV AH,AL ;Save old Divisor
- CALL WAIT
-
- INC DX
- IN AL,DX ;Save old Divisor
- MOV BX,AX
- CALL WAIT
-
- MOV DL,CL
- MOV AL,55H ;OUTPUT 55
- OUT DX,AL
- CALL WAIT
-
- INC DX
- MOV AL,0AAH
- OUT DX,AL ;OUTPUT AA
- CALL WAIT
-
- MOV DL,CL
- IN AL,DX ;Read New Divisor
- MOV AH,AL
- CALL WAIT
-
- INC DX
- IN AL,DX
- CMP AX,55AAH ;DO THEY MATCH?
- JZ Match
-
- XOR BP,BP ;Zero - no match
-
- Match: MOV DL,CL
- MOV AL,BH ;Restore old Divisor
- OUT DX,AL
- CALL WAIT
-
- INC DX
- MOV AL,BL
- OUT DX,AL
- CALL WAIT
-
- INC DX
- INC DX ;and old LCR
- MOV AL,CH
- OUT DX,AL
- CALL WAIT
-
- WAIT: RET
-
- ;---------
- ;PRINT SIGNON
- SINON: LEA DX,HELLO
- MOV AH,9
- INT 21H
- RET
-
- ;PRINT SIGNOFF
- SINOFF: LEA DX,BYE
- MOV AH,9
- INT 21H
- RET
-
- ; Now snatch the BIOS comm vector (Int 14)
-
- init_vectors:
-
- MOV AL,14H
- MOV AH,35H
- INT 21H
- MOV old_bios_vector,BX ; save old vector
- MOV old_bios_vector+2,ES
-
- MOV DX,OFFSET rsint ; Replace with our vector
- MOV AL,14H
- MOV AH,25H
- INT 21H
-
- ; Set the hardware interrupt vector
-
- XOR bp,bp ; Get a zero
- MOV DX,OFFSET serint_8250 ; Get our address
- MOV AL,hiv[BP]
- MOV AH,25H
- INT 21H
-
- ; Initialize the buffer ring pointers
-
- MOV AX,OFFSET i_buf ; Get start of buffer address
- ADD AX,BP ; include our offset
- MOV i_buf_in[BP],AX ; Put in the buffer pointers
- MOV i_buf_out[BP],AX ; Put in the buffer pointers
-
- ; Set the interrupt registers in the UART and clean things up
-
- CLI ; Disable interrupts
-
- IN AL,pic_mask_port ; Set up 8259 interupt controller
- AND AL,int_mask[BP] ; Enable the interrupts for this device
- OUT pic_mask_port,AL
-
- MOV CX,baseaddr[BP] ; Get base address for chip
-
- MOV DX,CX ; Compute port address for the IER
- ADD DX,ier_8250
- MOV AL,1 ; Enable data interrupt only
- OUT DX,AL
-
- MOV DX,CX ; Compute port address for the RBR
- ADD DX,rbr_8250
- IN AL,DX ; Read the input buffer and throw it away
-
- ; Enable interrupts
-
- STI ; Enable CPU to receive interupts
- RET
-
- everything ENDS
-
- END foo